﻿using System;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using O1;
using O1.Data;
using O1.Data.Traversal;
using O1.Data.Traversal.Fakes;

namespace Tests.O1.Data.Traversal
{
    [TestClass]
    public class CellObserverTests
    {
        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnBeginVertexInvokesFilterSetContext()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.StartVertexCellAddress = a =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnBeginVertex(new CellAddress())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnEndVertexInvokesFilterClearVertexState()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.EndVertexObservationInterests = (s) =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnEndVertex()).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnBeginInEdgeInvokesFilterSetContextWithInboundDirection()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.StartEdgeEdgeEdgeDirections = (e, d) =>
            {
                d.Should().Be(EdgeDirections.Inbound);
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnBeginInEdge(new Edge())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnBeginOutEdgeInvokesFilterSetContextWithOutboundDirection()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.StartEdgeEdgeEdgeDirections = (e, d) =>
            {
                d.Should().Be(EdgeDirections.Outbound);
                callCount++;
                return ObservationInterests.None;
            };

            InvokeObserver(stub, t => t.OnBeginOutEdge(new Edge())).ObservationState.Should().Be(ObservationInterests.None);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnEndEdgeInvokesFilterClearEdgeState()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EndEdgeObservationInterests = s =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnEndEdge()).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnPropertyInvokesEvaluateForDateProperty()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EvaluateDateProperty = p =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnProperty(new DateProperty())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnPropertyInvokesEvaluateForDateTimeProperty()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EvaluateDateTimeProperty = p =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnProperty(new DateTimeProperty())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnPropertyInvokesEvaluateForDecimalProperty()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EvaluateDecimalProperty = p =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnProperty(new DecimalProperty())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnPropertyInvokesEvaluateForHashProperty()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EvaluateHashProperty = p =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnProperty(new HashProperty())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnPropertyInvokesEvaluateForInt16Property()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EvaluateInt16Property = p =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnProperty(new Int16Property())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnPropertyInvokesEvaluateForInt32Property()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EvaluateInt32Property = p =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnProperty(new Int32Property())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnPropertyInvokesEvaluateForInt64Property()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EvaluateInt64Property = p =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnProperty(new Int64Property())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        [TestMethod, TestCategory("Unit Test")]
        public void CellObserverOnPropertyInvokesEvaluateForStringProperty()
        {
            int callCount = 0;
            var stub = new StubFilter();
            stub.PropertyInterestGet = () => PropertyInterests.All;
            stub.EvaluateStringProperty = p =>
            {
                callCount++;
                return ObservationInterests.Interested;
            };

            InvokeObserver(stub, t => t.OnProperty(new StringProperty())).ObservationState.Should().Be(ObservationInterests.Interested);
            callCount.Should().Be(1);
        }

        private static CellObserver InvokeObserver(Filter stub, Action<CellObserver> test)
        {
            var target = new CellObserver(stub);
            test(target);
            return target;
        }
    }
}
